using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Threading;
using PugLib;
using PugLib.Threading;

namespace ParallelTest
{
    public partial class Benchmarks
    {
        static int dividedItemCount;
        static int doneThreads;
        public const int ThreadScalar = 4;  // on the core 2 duo E4400, 4 threads per core is the sweetspot

        #region Threading

        public static TestResult ListIntGenericsThreads(int threadCount)
        {
            string testName = string.Format("ListIntGenericsThreads ({0} threads)", threadCount.ToString().PadLeft(3));
            Console.WriteLine(testName);
            doneThreads = 0; // reset end condition    
            sumItemCount = 0;
            TestResult trd = new TestResult(testName);

            // set up the distributed work load
            int totalCount = ItemCount;
            List<int> dividedItemCounts = new List<int>(threadCount);
            for (int i = 0; i < threadCount; i++)
            {
                dividedItemCounts.Add(0);
            }
            List<Thread> threads = new List<Thread>(threadCount);
            Dictionary<int, List<int>> lis = new Dictionary<int, List<int>>(threadCount);

            // partition the workload evenly between threads
            PartitionWorkLoad(threadCount, totalCount, ref dividedItemCounts);

            // set up threads
            for (int j = 0; j < threadCount; j++)
            {
                // add list
                lis.Add(j, new List<int>(dividedItemCounts[j]));

                // fill each list
                for (int i = 0; i < dividedItemCounts[j]; i++)
                {
                    lis[j].Add(1);
                }

                ListIntThreadData td = new ListIntThreadData(Guid.NewGuid(), lis[j]);
                ThreadWithState tws = new ThreadWithState(td, (data) =>
                {
                    sumItemCount += (long)data.Result;
                    doneThreads++;
                });

                Thread t = new Thread(new ThreadStart(tws.ThreadProc));
                t.IsBackground = true;

                threads.Add(t);
            }

            // spawn work
            stopwatch.Reset();
            stopwatch.Start();
            for (int j = 0; j < threads.Count; j++)
            {
                threads[j].Start();
            }

            // block until all threads finish
            while (doneThreads != threadCount) { }
            stopwatch.Stop();

            // fill return value
            trd.Duration = stopwatch.Elapsed.TotalSeconds;

            // cleanup
            lis.Clear();

            // summary
            Debug.Assert(sumItemCount == AssertValue);

            return trd;
        }

        private static void PartitionWorkLoad(int threadCount, int totalCount, ref List<int> dividedItemCounts)
        {
            while (totalCount > 0)
            {
                if (totalCount < threadCount)
                {
                    dividedItemCount = 1;
                }
                else
                {
                    dividedItemCount = totalCount / threadCount;
                }
                for (int i = 0; i < threadCount && totalCount > 0; i++)
                {
                    dividedItemCounts[i] += dividedItemCount;
                    totalCount -= dividedItemCount;
                }
            }
        }

        private static void ListIntGenericsThreadsCallBack(AbstractThreadData td)
        {
            sumItemCount += td.Result;
            doneThreads++;
        }

        #endregion

        #region BGWorker
        public static TestResult ListIntGenericsBGW(int threadCount)
        {
            string testName = string.Format("ListIntGenericsBGW ({0} threads)", threadCount.ToString().PadLeft(3));
            Console.WriteLine(testName);
            doneThreads = 0; // reset end condition    
            sumItemCount = 0;
            TestResult trd = new TestResult(testName);

            // set up the distributed work load
            int totalCount = ItemCount;
            List<int> dividedItemCounts = new List<int>(threadCount);
            for (int i = 0; i < threadCount; i++)
            {
                dividedItemCounts.Add(0);
            }

            List<BackgroundWorker> workers = new List<BackgroundWorker>(threadCount);
            Dictionary<int, List<int>> lis = new Dictionary<int, List<int>>(threadCount);

            // partition the workload evenly between threads
            PartitionWorkLoad(threadCount, totalCount, ref dividedItemCounts);

            // set up bgworkers
            for (int j = 0; j < threadCount; j++)
            {
                // hook up bgworkers
                BackgroundWorker bgw = new BackgroundWorker();
                bgw.DoWork += new DoWorkEventHandler(ListIntGenericsBGW_DoWork);
                bgw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(ListIntGenericsBGW_RunWorkerCompleted);
                workers.Add(bgw);

                // add list
                lis.Add(j, new List<int>(dividedItemCounts[j]));

                // fill each list
                for (int i = 0; i < dividedItemCounts[j]; i++)
                {
                    lis[j].Add(1);
                }
            }

            // spawn work
            stopwatch.Reset();
            stopwatch.Start();
            for (int j = 0; j < workers.Count; j++)
            {
                workers[j].RunWorkerAsync(new ListIntThreadData(Guid.NewGuid(), lis[j]));
            }

            // block until all threads finish
            while (doneThreads != threadCount) { }
            stopwatch.Stop();

            // fill return value
            trd.Duration = stopwatch.Elapsed.TotalSeconds;

            // cleanup
            workers.ForEach(delegate(BackgroundWorker bgw)
            {
                bgw.DoWork -= new DoWorkEventHandler(ListIntGenericsBGW_DoWork);
                bgw.RunWorkerCompleted -= new RunWorkerCompletedEventHandler(ListIntGenericsBGW_RunWorkerCompleted);
                bgw.Dispose();
            });
            lis.Clear();

            Debug.Assert(sumItemCount == AssertValue);
            return trd;
        }

        private static void ListIntGenericsBGW_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            sumItemCount += (e.Result as AbstractThreadData).Result;
            doneThreads++;
        }

        private static void ListIntGenericsBGW_DoWork(object sender, DoWorkEventArgs e)
        {
            ListIntThreadData td = e.Argument as ListIntThreadData;

            Int64 threadtotal = 0;
            for (int j = 0; j < Max; j++)
            {
                td.Items.ForEach(delegate(int foo)
                {
                    threadtotal += foo;
                });
            }
            e.Result = new ListIntThreadData(td.Id, threadtotal);

        }
        #endregion BGWorker

    }
}
